#######################################################
# Example of PingPong Game using FRTDEVS players
# Author: moonho.hwang@gmail.com
#   Date: Oct.19.2011
#######################################################
from XSY.DEVS.FDDEVS import *
from XSY.DEVS.CoupledDEVS import *
from XSY.DEVS.SimEngine import SimEngine
from XSY.DEVS.VeriEngine import VeriEngine

class Player(FDDEVS):
   '''A ping pong play using FDDEVS'''
   def __init__(m, n, iphase, st, parent=None):
      '''constructor'''
      FRTDEVS.__init__(m, n, parent) 
      m.iphase = iphase   # initial phase
      m.phase = m.iphase # set to the given inital state
      m.sendtime = st     # set the sending time

      m.addInputPorts('?r')
      m.addOutputPorts('!s')

   def delta_0(m):
      '''initial state'''
      m.phase = m.iphase # set to the given inital state

   def tau(m):
      '''lifespan scheduling function'''
      if m.phase == 'S':
         return m.sendtime # 
      else:
         return float('inf') # 

   def delta_x(m, x):
      '''external transition function'''
      if x.port == m.IP['?r'] and m.phase == 'W':
	 m.phase = 'S'
         return True
      else:
         return False
               
   def delta_y(m):
      '''output and internal transition function'''
      if m.phase == 'S':
	 m.phase = 'W'
         y = PortValue(m.OP["!s"])
         return y
   
   def get_state(m):
      '''get the state'''
      return m.phase

   def set_state(m, s):
      '''set the state as s'''
      m.phase = s
 
def makeModel():
   '''make the coupled model for pingpong'''
   md1 = Player('A', 'S', .1)
   md2 = Player('B', 'W', .2)

   cm = CoupledDEVS("")   # try it out "PingPong and see the result"
   cm.addComponents(md1, md2) 
   cm.addCouples((md1.OP["!s"], md2.IP["?r"]))

   # If you comment out the following line, you will see a live-lock system. 
   cm.addCouples((md2.OP["!s"], md1.IP["?r"]))  
   return cm

###########################################################################################################
def runSimulation(md):
   '''run a simulation thread'''
   engine = SimEngine(md)

   #---- setting StepByStep and DES simulation only ----
   engine.StepByStep = True
   engine.sfactor = float('inf')
   #----------------------------------------------------
   engine.runInteractiveMenu()

############################################################################################################
def checkLiveness(KDAG):
   '''check if sc if kernel DAG is alive'''
   for k in KDAG.V:
      sc = KDAG.kv[k] # for each vertex cv in KDAG.V
      if len(sc)>1: # multiple vertex in it
         model_E = {} # check model n true for having 'E' state
	 model_S = {} # check model n true for having 'S' state
         for v in sc: # for each vertex v in sc
            for n in v.s: # for each model n in v
               if n not in model_E: model_E[n] = False # initialization
               if n not in model_S: model_S[n] = False # initialization
               if v.s[n][0][0] == 'W': # the  first [0]: discrete state s, [1] lifetime ts (see XSY\DEVS\ReachGen.py)
	                               # the second [0]: phase (see get_state(m))
                  model_E[n] = True 
               elif v.s[n][0][0] == 'S':# the first [0]: discrete state s, [1] lifetime ts (see XSY\DEVS\ReachGen.py)
	                                # the second [0]: phase (see get_state(m)) 
                  model_S[n] = True
         all_meet = True
         for n in model_E:
            if not(model_E[n] and model_S[n]):
               all_meet = False
               break
         if all_meet:
            return True 
   return False # there is a kernel vertex satisfying the condition 

def runVerification(md):
   '''run a verification thread'''
   X = [] # no input needs to be tested.
     
   VE = VeriEngine(md, X, checkdeadlockCB=None, checklivenessCB=checkLiveness)
   VE.runtimeInteractiveMenu()

if __name__ == '__main__':
   md = makeModel()
   print 'isNetworkOf(Player)=', md.isNetworkOf(Player)

   c = True
   while c:
      s = raw_input('(s)im, (v)eri, e(x)it>')
      if s == 's' or s == 'sim': 
         runSimulation(md)
      elif s == 'v' or s == 'veri':
         runVerification(md)
      elif s == 'x' or s == 'exit':
         c = False
      else:
         print('%s is an invalid input!'%s)
      
   print "good bye~!" 
